package com.hbxs.folder.modules.folder.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.tobato.fastdfs.domain.fdfs.FileInfo;
import com.github.tobato.fastdfs.domain.fdfs.MetaData;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.domain.proto.storage.DownloadByteArray;
import com.github.tobato.fastdfs.domain.upload.FastFile;
import com.github.tobato.fastdfs.domain.upload.FastImageFile;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.google.common.collect.Lists;

import com.hbxs.folder.bean.CustomGenericException;
import com.hbxs.folder.bean.FileBean;
import com.hbxs.folder.modules.folder.service.FastFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

/**
 * @author Radon Galleger
 * @ClassName: FastFileServiceImpl
 * @Package com.hbxs.acs.service.impl
 * @Description: USEFULNESS()
 * @date 2020/6/2 14:12
 */
@Slf4j
@Service
public class FastFileServiceImpl implements FastFileService {
    @Autowired
    protected FastFileStorageClient storageClient;
    @Autowired
    protected AppendFileStorageClient appendFileStorageClient;

    /**
     * 上传文件
     * @param fileBean
     * @return
     */
    @Override
    public FileBean uploadFile(FileBean fileBean) throws CustomGenericException, IOException {
        FastFile fastFile = new FastFile.Builder()
                .withFile(fileBean.getInputStream(), fileBean.getSize(), fileBean.getSuffixName())
                .withMetaData(fileBean.getMetaDataSet())
                .toGroup(fileBean.getGroup())
                .build();
        log.debug("##上传文件..##"+ JSON.toJSONString(fileBean));
        StorePath path = storageClient.uploadFile(fastFile);
        fileBean.getInputStream().close();
        if (Objects.isNull(path)){
            log.error("##上传文件失败##"+ JSON.toJSONString(fileBean));
            throw CustomGenericException.generate(9002,"文件上传错误");
        }
        fileBean.setPath(path.getPath());
        fileBean.setGroup(path.getGroup());
        return fileBean;
    }

    /**
     * 获取文件元数据
     * @param fileBean
     * @return
     */
    @Override
    public FileBean getMetaData(FileBean fileBean){
        log.debug("##获取文件元数据..##"+ JSON.toJSONString(fileBean));
        Set<MetaData> fetchMetaData = storageClient.getMetadata(fileBean.getGroup(), fileBean.getPath());
        fileBean.setMetaDataSet(fetchMetaData);
        return fileBean;
    }

    /**
     * 获取文件信息
     * @param fileBean
     * @return
     */
    @Override
    public FileBean getFileInfo(FileBean fileBean){
        log.debug("##获取文件元数据..##"+ JSON.toJSONString(fileBean));
        FileInfo fileInfo = storageClient.queryFileInfo(fileBean.getGroup(), fileBean.getPath());
        fileBean.setSize(fileInfo.getFileSize());
        fileBean.setCreateTime(new Date(fileInfo.getCreateTime()));
        fileBean.setCrc32(fileInfo.getCrc32());
        fileBean.setSourceIpAddr(fileInfo.getSourceIpAddr());
        return fileBean;
    }

    /**
     * 生成文件元数据
     * @param fileBean
     * @return
     */
    @Override
    public FileBean overwriteMetadata(FileBean fileBean){
        log.debug("##生成文件元数据..##"+ JSON.toJSONString(fileBean));
        storageClient.overwriteMetadata(fileBean.getGroup(), fileBean.getPath(), fileBean.getMetaDataSet());
        Set<MetaData> fetchMetaData = storageClient.getMetadata(fileBean.getGroup(), fileBean.getPath());
        fileBean.setMetaDataSet(fetchMetaData);
        return fileBean;
    }

    /**
     * 生成文件元数据
     * @param fileBean
     * @return
     */
    @Override
    public FileBean mergeMetadata(FileBean fileBean){
        log.debug("##生成文件元数据..##"+ JSON.toJSONString(fileBean));
        storageClient.mergeMetadata(fileBean.getGroup(), fileBean.getPath(), fileBean.getMetaDataSet());
        Set<MetaData> fetchMetaData = storageClient.getMetadata(fileBean.getGroup(), fileBean.getPath());
        fileBean.setMetaDataSet(fetchMetaData);
        return fileBean;
    }

    /**
     * 删除文件
     * @param fileBean
     */
    @Override
    public void deleteFile(FileBean fileBean){
        log.debug("##删除文件..##"+ fileBean.getPath());
        storageClient.deleteFile(fileBean.getGroup(), fileBean.getPath());
    }

    /**
     * 上传图片
     * @param fileBean
     * @return
     */
    @Override
    public FileBean uploadImage(FileBean fileBean) throws CustomGenericException, IOException {

        FastImageFile fastImageFile = new FastImageFile.Builder()
                .withFile(fileBean.getInputStream(), fileBean.getSize(), fileBean.getSuffixName())
                .withMetaData(fileBean.getMetaDataSet())
                .toGroup(fileBean.getGroup())
                .build();
        log.debug("##上传图片..##"+ JSON.toJSONString(fileBean));
        StorePath path = storageClient.uploadImage(fastImageFile);
        fileBean.getInputStream().close();
        if (Objects.isNull(path)){
            log.error("##上传文件失败##"+ JSON.toJSONString(fileBean));
            throw CustomGenericException.generate(9002,"文件上传错误");
        }
        fileBean.setPath(path.getPath());
        fileBean.setGroup(path.getGroup());
        return fileBean;
    }

    /**
     * 上传图片生成缩略图（按缩略图默认配置）
     * @param fileBean
     * @return
     */
    @Override
    public FileBean uploadImageAndCrtThumbImageByDefault(FileBean fileBean) throws CustomGenericException, IOException {

        FastImageFile fastImageFile = new FastImageFile.Builder()
                .withThumbImage()
                .withFile(fileBean.getInputStream(), fileBean.getSize(), fileBean.getSuffixName())
                .withMetaData(fileBean.getMetaDataSet())
                .toGroup(fileBean.getGroup())
                .build();
        log.debug("##上传图片..##"+ JSON.toJSONString(fileBean));
        StorePath path = storageClient.uploadImage(fastImageFile);
        fileBean.getInputStream().close();
        if (Objects.isNull(path)){
            log.error("##上传文件失败##"+ JSON.toJSONString(fileBean));
            throw CustomGenericException.generate(9002,"文件上传错误");
        }
        //从文件
        List<FileBean> fileBeanList = Lists.newArrayList();
        FileBean abbreviation = new FileBean();
        abbreviation.setPath(fastImageFile.getThumbImagePath(path.getPath()));
        abbreviation.setGroup(fileBean.getGroup());
        fileBeanList.add(abbreviation);
        fileBean.setPath(path.getPath());
        fileBean.setGroup(path.getGroup());
        fileBean.setFileBeanList(fileBeanList);
        return fileBean;
    }

    /**
     * 上传图片生成缩略图（按缩略图设定尺寸）
     * @param fileBean
     * @return
     */
    @Override
    public FileBean uploadImageAndCrtThumbImageBySize(FileBean fileBean, Integer width, Integer height) throws CustomGenericException, IOException {

        FastImageFile fastImageFile = new FastImageFile.Builder()
                .withThumbImage(width,height)
                .withFile(fileBean.getInputStream(), fileBean.getSize(), fileBean.getSuffixName())
                .withMetaData(fileBean.getMetaDataSet())
                .toGroup(fileBean.getGroup())
                .build();
        log.debug("##上传图片..##"+ JSON.toJSONString(fileBean));
        StorePath path = storageClient.uploadImage(fastImageFile);
        fileBean.getInputStream().close();
        if (Objects.isNull(path)){
            log.error("##上传文件失败##"+ JSON.toJSONString(fileBean));
            throw CustomGenericException.generate(9002,"文件上传错误");
        }
        //从文件
        List<FileBean> fileBeanList = Lists.newArrayList();
        FileBean abbreviation = new FileBean();
        abbreviation.setPath(fastImageFile.getThumbImagePath(path.getPath()));
        abbreviation.setGroup(fileBean.getGroup());
        fileBeanList.add(abbreviation);
        fileBean.setPath(path.getPath());
        fileBean.setGroup(path.getGroup());
        fileBean.setFileBeanList(fileBeanList);
        return fileBean;
    }

    /**
     * 上传图片生成缩略图（按缩略图设定尺寸）
     * @param fileBean
     * @return
     */
    @Override
    public FileBean uploadImageAndCrtThumbImageByScale(FileBean fileBean, Double percent) throws CustomGenericException, IOException {

        FastImageFile fastImageFile = new FastImageFile.Builder()
                .withThumbImage(percent)
                .withFile(fileBean.getInputStream(), fileBean.getSize(), fileBean.getSuffixName())
                .withMetaData(fileBean.getMetaDataSet())
                .toGroup(fileBean.getGroup())
                .build();
        log.debug("##上传图片..##"+ JSON.toJSONString(fileBean));
        StorePath path = storageClient.uploadImage(fastImageFile);
        fileBean.getInputStream().close();
        if (Objects.isNull(path)){
            log.error("##上传文件失败##"+ JSON.toJSONString(fileBean));
            throw CustomGenericException.generate(9002,"文件上传错误");
        }
        //从文件
        List<FileBean> fileBeanList = Lists.newArrayList();
        FileBean abbreviation = new FileBean();
        abbreviation.setPath(fastImageFile.getThumbImagePath(path.getPath()));
        abbreviation.setGroup(fileBean.getGroup());
        fileBeanList.add(abbreviation);
        fileBean.setPath(path.getPath());
        fileBean.setGroup(path.getGroup());
        fileBean.setFileBeanList(fileBeanList);
        return fileBean;
    }

    /**
     * 上传从文件
     * @param fileBean
     * @return
     */
    @Override
    public FileBean uploadSlaveFile(FileBean fileBean) throws CustomGenericException, IOException {
        log.debug("##上传从文件..##"+ JSON.toJSONString(fileBean));
        for (Iterator iterator = fileBean.getFileBeanList().iterator(); iterator.hasNext(); ) {
            FileBean next = (FileBean) iterator.next();
            StorePath path = storageClient.uploadSlaveFile(fileBean.getGroup(), fileBean.getPath(), next.getInputStream(),
                    next.getSize(), next.getPrefixName(), next.getSuffixName());
            next.getInputStream().close();
            if (Objects.isNull(path)){
                log.error("##上传文件失败##"+ JSON.toJSONString(next));
                throw CustomGenericException.generate(9002,"文件上传错误");
            }
            next.setPath(path.getPath());
            next.setGroup(path.getGroup());
        }
        return fileBean;
    }

    /**
     * 下载文件
     * @param fileBean
     * @return
     */
    @Override
    public FileBean downloadFile(FileBean fileBean) {
        DownloadByteArray callback = new DownloadByteArray();
        byte[] content = storageClient.downloadFile(fileBean.getGroup(), fileBean.getPath(), callback);
        fileBean.setInputStream(new ByteArrayInputStream(content));
        return fileBean;
    }

    /**
     * 下载文件片段
     * @param fileBean
     * @param fileOffset 开始位置
     * @return
     */
    @Override
    public FileBean downloadFile(FileBean fileBean, Long fileOffset) {
        DownloadByteArray callback = new DownloadByteArray();
        byte[] content = storageClient.downloadFile(fileBean.getGroup(),
                fileBean.getPath(), fileOffset, fileBean.getSize(), callback);
        fileBean.setInputStream(new ByteArrayInputStream(content));
        return fileBean;
    }

    /**
     * 断点续传(上传第一段)
     * @param fileBean
     * @return
     */
    @Override
    public FileBean uploadAppenderFile(FileBean fileBean) throws CustomGenericException, IOException {
        log.debug("##断点续传(上传第一段)..##"+ JSON.toJSONString(fileBean));
        StorePath path = appendFileStorageClient.uploadAppenderFile(fileBean.getGroup(), fileBean.getInputStream(),
                fileBean.getSize(), fileBean.getSuffixName());
        fileBean.getInputStream().close();
        if (Objects.isNull(path)){
            log.error("##断点续传(上传第一段)##"+ JSON.toJSONString(fileBean));
            throw CustomGenericException.generate(9002,"文件上传错误");
        }
        fileBean.setPath(path.getPath());
        fileBean.setGroup(path.getGroup());
        return fileBean;
    }

    /**
     * 断点续传(上传其它段)
     * @param fileBean
     * @return
     */
    @Override
    public FileBean appendFile(FileBean fileBean) throws IOException {

        appendFileStorageClient.appendFile(fileBean.getGroup(), fileBean.getPath(), fileBean.getInputStream(),
                fileBean.getSize());
        fileBean.getInputStream().close();
        return fileBean;
    }

    /**
     * 清除续传类型文件
     * @param fileBean
     * @return
     */
    @Override
    public FileBean truncateAppendFile(FileBean fileBean) {
        appendFileStorageClient.truncateFile(fileBean.getGroup(), fileBean.getPath(), 0);
        return fileBean;
    }

    /**
     * 重新上传续传类型文件
     * @param fileBean
     * @param fileOffset 开始位置（大小截取）
     * @return
     * @throws IOException
     */
    @Override
    public FileBean modifyAppendFile(FileBean fileBean, Long fileOffset) throws IOException {
        appendFileStorageClient.modifyFile(fileBean.getGroup(), fileBean.getPath(), fileBean.getInputStream(), fileBean.getSize(),
                fileOffset);
        fileBean.getInputStream().close();
        return fileBean;
    }
}
